#include <assert.h>

#include "SignatureManager.h"
#include <QMessageAuthenticationCode>

using namespace std;

namespace TCServer
{
    namespace Manager
    {
        const QByteArray SignatureManager::key = "Test Key From Weizehua";

        SignatureManager* SignatureManager::_instance = nullptr;

        SignatureManager::SignatureManager()
        {
            assert(_instance == nullptr);

            _instance = this;
        }

        SignatureManager::~SignatureManager()
        {
            _instance = nullptr;
        }

        SignatureManager* SignatureManager::instance()
        {
            if (_instance == nullptr)
                _instance = new SignatureManager;
            return _instance;
        }

        QByteArray SignatureManager::generateFromUserPermissions(quint64 userId, const QSet<Type::Permission> &permissions)
        {
            QByteArray data;
            {
                QDataStream stream(&data, QIODevice::WriteOnly);
                stream << userId << permissions;
            }
            QByteArray mac = QMessageAuthenticationCode::hash(data, key, QCryptographicHash::Algorithm::Md5);

            QByteArray res;
            QDataStream stream(&res, QIODevice::WriteOnly);
            stream << data << move(mac);
            return res;
        }

        std::tuple<bool,
            quint64,
            QSet<Type::Permission>>
            SignatureManager::check(const QByteArray &signature)
        {
            try
            {
                do 
                {
                    if (signature.isEmpty())
                        break;
                    QByteArray data;
                    QByteArray mac;
                    {
                        QDataStream stream(signature);
                        stream >> data >> mac;
                    }

                    bool pass = QMessageAuthenticationCode::hash(data, key, QCryptographicHash::Algorithm::Md5) == mac;
                    quint64 userId;
                    QSet<Type::Permission> permissions;

                    QDataStream stream(data);
                    stream >> userId >> permissions;

                    return make_tuple(pass, userId, move(permissions));
                } while (0);
            }
            catch (...)
            {
            }
            static const auto failedRes = std::tuple<bool, quint64, QSet<Type::Permission>>();
            return failedRes;
        }

    }
}